package com.grace.cms.forum.user.security.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Component;

/**
 * redis保存用户token
 * <p>
 * remember me
 * <p>
 * {@link org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl}
 * {@link org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices}
 * {@link org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken}
 * PersistentRememberMeToken 没有实现Serializable，无法进行序列化，自定义存储数据结构
 */
@Component
public class RedisTokenRepositoryImpl implements PersistentTokenRepository {

    private static final Logger log = LoggerFactory.getLogger(RedisTokenRepositoryImpl.class);
    
    @Autowired
    private RedisTemplate redisTemplate;

    // 令牌过期时间
    private final static long TOKEN_VALID_DAYS = 5;

    @Override
    public void createNewToken(PersistentRememberMeToken token) {
        String key = generateKey(token.getSeries());
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("username", token.getUsername());
        map.put("tokenValue", token.getTokenValue());
        map.put("date", String.valueOf(token.getDate().getTime()));
        redisTemplate.opsForHash().putAll(key, map);
        redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
    }

    @Override
    public void updateToken(String series, String tokenValue, Date lastUsed) {
        String key = generateKey(series);
        HashMap<String, String> map = new HashMap<String, String> ();
        map.put("tokenValue", tokenValue);
        map.put("date", String.valueOf(lastUsed.getTime()));
        redisTemplate.opsForHash().putAll(key, map);
        redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
    }

    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        String key = generateKey(seriesId);
        // 使用单独的get操作获取每个字段值，避免类型转换问题
        String username = (String) redisTemplate.opsForHash().get(key, "username");
        String tokenValue = (String) redisTemplate.opsForHash().get(key, "tokenValue");
        String date = (String) redisTemplate.opsForHash().get(key, "date");
        if (null == username || null == tokenValue || null == date) {
            return null;
        }
        Long timestamp = Long.valueOf(date);
        Date time = new Date(timestamp);
        PersistentRememberMeToken token = new PersistentRememberMeToken(username, seriesId, tokenValue, time);
        return token;
    }

    @Override
    public void removeUserTokens(String username) {
        // 参数校验
        if (username == null || username.isEmpty()) {
            return;
        }
        
        try {
            // 获取所有匹配的键
            String pattern = generateKey("*");
            Set<String> keys = redisTemplate.keys(pattern);
            
            // 如果没有匹配的键，直接返回
            if (keys == null || keys.isEmpty()) {
                return;
            }
            
            // 收集需要删除的键
            List<String> keysToDelete = new ArrayList<>();
            for (String key : keys) {
                Object storedUsername = redisTemplate.opsForHash().get(key, "username");
                if (username.equals(storedUsername)) {
                    keysToDelete.add(key);
                }
            }
            
            // 批量删除所有匹配的token
            if (!keysToDelete.isEmpty()) {
                redisTemplate.delete(keysToDelete);
            }
        } catch (Exception e) {
            // 记录异常但不中断操作
            log.error("Error removing user tokens for username: {}", username, e);
        }
    }

    /**
     * 生成key
     *
     * @param series
     * @return
     */
    private String generateKey(String series) {
        return "spring:security:rememberMe:token:" + series;
    }
}